/*
 * NewFileDialog.java
 *
 *  
 */
package org.msb.finance.app;

import static java.awt.GridBagConstraints.HORIZONTAL;
import static java.awt.GridBagConstraints.NONE;
import static java.awt.GridBagConstraints.NORTHWEST;
import static java.awt.GridBagConstraints.SOUTH;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;
import java.util.prefs.Preferences;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/**
 * 
 * 
 * @author Marc Boudreau
 * 
 */
@SuppressWarnings("serial")
public class NewFileDialog extends JDialog {

	/*
	 * 
	 */
	private JTextField nameField;

	/*
	 * 
	 */
	private JFileChooser fileChooser;

	/**
	 * 
	 */
	private JPasswordField passwordField;

	/**
	 * 
	 */
	private JPasswordField passwordConfirmField;

	/**
	 * 
	 */
	private JLabel strengthLabel;

	/*
	 * 
	 */
	private Preferences prefs;

	/**
	 * 
	 * @param parent
	 */
	public NewFileDialog(Frame parent) {
		super(parent, true);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.JDialog#dialogInit()
	 */
	@Override
	protected void dialogInit() {
		super.dialogInit();

		setContentPane(createContentPane());
		setTitle(FinanceApp.getMessageResources().getString("dialog.newfile.title")); //$NON-NLS-1$

		pack();
	}

	private JComponent createContentPane() {
		JPanel contentPane = new JPanel(new GridBagLayout());
		contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		contentPane
				.add(
						new JLabel(FinanceApp.getMessageResources().getString("dialog.newfile.message.text")), new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, NORTHWEST, NONE, new Insets(0, 0, 0, 0), 0, 0)); //$NON-NLS-1$

		contentPane
				.add(
						new JLabel(FinanceApp.getMessageResources().getString("dialog.newfile.username.caption")), new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0, NORTHWEST, NONE, new Insets(5, 0, 0, 0), 0, 0)); //$NON-NLS-1$

		contentPane.add(getNameField(), new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0, NORTHWEST, HORIZONTAL, new Insets(
				0, 20, 0, 0), 0, 0));

		contentPane.add(getBrowseButton(), new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, NORTHWEST, NONE, new Insets(0,
				5, 0, 0), 0, 0));

		contentPane
				.add(
						new JLabel(FinanceApp.getMessageResources().getString("dialog.newfile.password.caption")), new GridBagConstraints(0, 3, 2, 1, 0.0, 0.0, NORTHWEST, NONE, new Insets(5, 0, 0, 0), 0, 0));//$NON-NLS-1$

		this.passwordField = new JPasswordField(20);
		contentPane.add(this.passwordField, new GridBagConstraints(0, 4, 1, 1, 1.0, 0.0, NORTHWEST, HORIZONTAL,
				new Insets(0, 20, 0, 0), 0, 0));

		contentPane.add(
				new JLabel(FinanceApp.getMessageResources().getString("dialog.newfile.passwordconfirm.caption")), //$NON-NLS-1$
				new GridBagConstraints(0, 5, 2, 1, 0.0, 0.0, NORTHWEST, NONE, new Insets(5, 0, 0, 0), 0, 0));

		this.passwordConfirmField = new JPasswordField(20);
		contentPane.add(this.passwordConfirmField, new GridBagConstraints(0, 6, 1, 1, 1.0, 0.0, NORTHWEST, HORIZONTAL,
				new Insets(0, 20, 0, 0), 0, 0));

		this.strengthLabel = new JLabel(FinanceApp.getMessageResources().getString(
				"dialog.newfile.passwordstrength.invalid.caption"), FinanceApp.getMessageResources().getIcon( //$NON-NLS-1$
				"dialog.newfile.passwordstrength.invalid.icon"), SwingConstants.LEADING); //$NON-NLS-1$
		contentPane.add(this.strengthLabel, new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0, NORTHWEST, NONE, new Insets(5,
				0, 0, 0), 0, 0));

		contentPane.add(createButtonPanel(), new GridBagConstraints(0, 8, 2, 1, 0.0, 1.0, SOUTH, NONE, new Insets(10,
				0, 0, 0), 0, 0));

		return contentPane;
	}

	/*
	 * Retrieves the name text field. It is initialized if it has yet to be done.
	 */
	private JTextField getNameField() {
		if (null == this.nameField) {
			this.nameField = new JTextField(20);
		}

		return this.nameField;
	}

	private JButton getBrowseButton() {
		Action browseAction = new Action(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				JFileChooser fileChooser = getFileChooser();
				String curDir = getPreferences().get("NewFileDialog.NewDirectory.Path", null); //$NON-NLS-1$

				fileChooser.setCurrentDirectory(null == curDir ? null : new File(curDir));
				if (JFileChooser.APPROVE_OPTION == fileChooser.showOpenDialog(getNameField())) {
					File file = fileChooser.getSelectedFile();
					getPreferences().put("NewFileDialog.NewDirectory.Path", file.getParent()); //$NON-NLS-1$
					getNameField().setText(file.getPath());
				}
			}
		}, FinanceApp.getMessageResources(), "button.browse", "browse", true, false, null); //$NON-NLS-1$ //$NON-NLS-2$
		JButton button = new JButton(browseAction);
		button.setMargin(new Insets(2, 2, 2, 2));
		Dimension d = button.getPreferredSize();
		d.height = getNameField().getPreferredSize().height;
		button.setPreferredSize(d);

		return button;
	}

	private JFileChooser getFileChooser() {
		if (null == this.fileChooser) {
			this.fileChooser = new FinanceFileChooser();
		}

		return this.fileChooser;
	}

	/**
	 * 
	 * @return
	 */
	private JComponent createButtonPanel() {
		JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 5, 0));

		ActionListener buttonHandler = new ButtonActionHandler();
		Action okAction = new Action(buttonHandler, FinanceApp.getMessageResources(), "button.ok", "ok", //$NON-NLS-1$ //$NON-NLS-2$
				false, false, new NewFileDialogFieldsCallBack());
		this.nameField.getDocument().addDocumentListener(new DocumentChangeListener(this.nameField, okAction));
		this.passwordField.getDocument().addDocumentListener(new DocumentChangeListener(this.passwordField, okAction));
		this.passwordConfirmField.getDocument().addDocumentListener(
				new DocumentChangeListener(this.passwordConfirmField, okAction));
		this.passwordField.getDocument().addDocumentListener(new PasswordStrengthListener(this.strengthLabel));
		JButton okButton = new JButton(okAction);

		buttonPanel.add(okButton);

		getRootPane().setDefaultButton(okButton);

		JButton cancelButton = new JButton(new Action(buttonHandler, FinanceApp.getMessageResources(), "button.cancel", //$NON-NLS-1$
				"cancel", true, false, null)); //$NON-NLS-1$
		buttonPanel.add(cancelButton);

		return buttonPanel;
	}

	/**
	 * 
	 * @return s
	 */
	public File getFile() {
		return new File(this.nameField.getText());
	}

	/**
	 * 
	 * @return s
	 */
	public char[] getPassword() {
		return this.passwordField.getPassword();
	}

	/**
	 * 
	 */
	public void erasePasswords() {
		this.passwordField.setText(null);
		this.passwordConfirmField.setText(null);
	}

	/*
	 * 
	 */
	private Preferences getPreferences() {
		if (null == this.prefs) {
			this.prefs = Preferences.userNodeForPackage(FinanceApp.class);
		}

		return this.prefs;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.Dialog#setVisible(boolean)
	 */
	@Override
	public void setVisible(boolean b) {
		/*
		 * Reset any previous Credential when making this dialog visible.
		 */
		if (b) {
			this.nameField.setText(null);
			erasePasswords();
		}

		super.setVisible(b);
	}

	/**
	 * 
	 * 
	 * 
	 * @author boudream
	 * 
	 */
	private class NewFileDialogFieldsCallBack implements ActionEnabledCallback {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.msb.finance.app.ActionEnabledCallback#isEnabled()
		 */
		public boolean isEnabled() {
			return 0 < NewFileDialog.this.nameField.getText().length()
					&& 0 < NewFileDialog.this.passwordField.getPassword().length
					&& Arrays.equals(NewFileDialog.this.passwordField.getPassword(),
							NewFileDialog.this.passwordConfirmField.getPassword());
		}
	}

	/*
	 * 
	 */
	private class ButtonActionHandler implements ActionListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent e) {
			String command = e.getActionCommand();

			if (null == command) {
				return;
			}

			if (command.equals("cancel")) { //$NON-NLS-1$
				NewFileDialog.this.nameField.setText(null);
				erasePasswords();
			}

			setVisible(false);
		}
	}

	/*
	 * 
	 */
	private class PasswordStrengthListener implements DocumentListener {

		private JLabel strengthLabel;

		private PasswordStrengthListener(JLabel label) {
			this.strengthLabel = label;
		}

		/*
		 * 
		 */
		private void handleChangeEvent() {
			char[] password = NewFileDialog.this.passwordField.getPassword();
			String strength = evaluateStrength(password);

			this.strengthLabel.setIcon(FinanceApp.getMessageResources().getIcon(
					"dialog.newfile.passwordstrength." + strength + ".icon")); //$NON-NLS-1$ //$NON-NLS-2$
			this.strengthLabel.setText(FinanceApp.getMessageResources().getString(
					"dialog.newfile.passwordstrength." + strength + ".caption")); //$NON-NLS-1$ //$NON-NLS-2$
		}

		/*
		 * 
		 */
		private String evaluateStrength(char[] password) {
			int letters = 0;
			int digits = 0;
			int symbols = 0;
			int length = 0;

			for (char c : password) {
				if (Character.isDigit(c)) {
					++digits;
				} else if (Character.isLetter(c)) {
					++letters;
				} else {
					++symbols;
				}

				++length;
			}

			Arrays.fill(password, '\0');

			// The number of different categories of characters used.
			int charCategoriesUsed = 0;
			if (0 < letters) {
				++charCategoriesUsed;
			}
			if (0 < digits) {
				++charCategoriesUsed;
			}
			if (0 < symbols) {
				++charCategoriesUsed;
			}

			if (0 == length || 0 == charCategoriesUsed) {
				return "invalid"; //$NON-NLS-1$
			} else if (8 <= length && 2 <= charCategoriesUsed) {
				return "strong"; //$NON-NLS-1$
			} else if (8 <= length && 1 == charCategoriesUsed || 4 <= length && 2 <= charCategoriesUsed) {
				return "normal"; //$NON-NLS-1$
			} else {
				return "weak"; //$NON-NLS-1$
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.DocumentListener#changedUpdate(javax.swing.event.DocumentEvent)
		 */
		public void changedUpdate(DocumentEvent e) {
			handleChangeEvent();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEvent)
		 */
		public void insertUpdate(DocumentEvent e) {
			handleChangeEvent();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEvent)
		 */
		public void removeUpdate(DocumentEvent e) {
			handleChangeEvent();
		}

	}

	/*
	 * 
	 */
	private class DocumentChangeListener implements DocumentListener {

		private EnabledStateChangeListener listener;

		private Object source;

		private DocumentChangeListener(Object source, EnabledStateChangeListener listener) {
			this.source = source;
			this.listener = listener;
		}

		/**
		 * 
		 */
		protected void fireChangeEvent() {
			ChangeEvent event = new ChangeEvent(this.source);

			this.listener.stateChanged(event);

		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.DocumentListener#changedUpdate(javax.swing.event.DocumentEvent)
		 */
		public void changedUpdate(DocumentEvent e) {
			fireChangeEvent();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEvent)
		 */
		public void insertUpdate(DocumentEvent e) {
			fireChangeEvent();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEvent)
		 */
		public void removeUpdate(DocumentEvent e) {
			fireChangeEvent();
		}
	}
}
